package vethbr

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"strings"
)

// 管理ip的分配和回收
// 实现 IPManager 接口
type SimpleIPHandler struct {
	// 键是 192.168.1.0/24 这种
	// 值是 一个用0和1表示的字符串，字符串的目的是表示第n个ip是否被分配了
	// -eg. 如上示例因为掩码是24，所以可分配的ip范围是 192.168.1.1~192.168.1.254 这255个
	//     那么我们的01字符串标识初始状态下就是 255个0，表示从1到254
	// -- 192.168.1.255 是个广播IP，不能分配
	// -- 192.168.1.0 用来标识整个子网，不能分配
	//
	// -当前用字符串表示有点浪费空间，比如255个0就占据255Byte，其实可以用4个int64的数字来表示,这样只占据32个Byte，算了简单点整吧
	Subnets map[string]string
}

func NewSimpleIPHandler() *SimpleIPHandler {
	return NewByConfig()
}

func NewByConfig() *SimpleIPHandler {
	// 从配置文件读出来已经存在的数据
	if err := initIPAMConfig(); err != nil {
		log.Printf("初始化IPAM配置文件失败...%v\n", err)
		return nil
	}

	configByte, err := readIPAMConfig()
	if err != nil {
		log.Printf("读取IPAM配置文件失败...%v\n", err)
		return nil
	}

	handler := &SimpleIPHandler{
		Subnets: make(map[string]string),
	}
	if len(configByte) == 0 {
		return handler
	}
	log.Println(string(configByte))
	if err := json.Unmarshal(configByte, &handler.Subnets); err != nil {
		log.Printf("解析IPAM配置文件失败...%v\n", err)
		return nil
	}
	return handler
}

func (handler *SimpleIPHandler) Allocate(subnet *net.IPNet) (ip net.IP, err error) {
	if err := initIPAMConfig(); err != nil {
		return nil, err
	}
	log.Printf("SUBNET: IP:%s\n", subnet.IP.String())
	// subnet.String() 返回的是 192.168.2.0/24
	subnetStr := subnet.String()
	log.Printf("SUBNET_STR: %#v\n", subnetStr)
	// Size() 方法返回掩码 255.255.255.0 的前导1的数量和总的bit数量，所以此处是 24和32
	leadingOne, totalBit := subnet.Mask.Size()
	validBit := totalBit - leadingOne
	length := 1 << validBit
	// 不存在该网段的话，就新增到map中
	if _, exist := handler.Subnets[subnetStr]; !exist {
		// 如上所示有8位可用长度的话，就意味着有2^8个连续的可用标识，我们将这些标识都初始化为0
		handler.Subnets[subnetStr] = strings.Repeat("0", length)
	}
	log.Println(handler.Subnets[subnetStr])
	// 现在都存在了，我们从中找到第一个可用的分配出去
	// 注意位置0（网段标识）和位置255（广播地址）不能分配出去
	for i := 0; i < length; i++ {
		if i == 0 || i == 255 {
			continue
		}
		pos := i
		if handler.Subnets[subnetStr][pos] == '0' {
			log.Printf("pos: %d\n", pos)
			// 网段IP - 192.168.2.0 的字节切片
			// !!! 好像默认情况下 subnet.IP 会以IPV6的形式存储，此处直接强制使用IPV4;
			// !!! IPV6导致我们的程序无法使用!!!
			ip = subnet.IP.To4()
			// 分配出去
			// 新的IP的计算方式如果是按照数字来说的话就是 网段 + pos
			// 但是 subnet.IP 这个地址段是存储的4个Byte，那就按照Byte来搞
			// 看样子 subnet.IP 的存储顺序是网络字节序，也就是常见的标识格式
			// 比如： 10.200.0.1
			// 转换成十六进制是 10对应十六进制0x0A ，200对应0xC8，0对应0x00，1对应0x01。
			// 存储在字节切片中就按照这个顺序 [0x0A、0xC8、0x00、0x01]
			// 也就是 高位字节(10) 存储在内存地位处(slice(0))
			// 所以我们如果要计算的话可以按照字节来逐个对应
			// 比如如果此处的 pos 是 258，则意味着他的二进制是
			// 00000000 00000000 00000001 00000011 (网络字节序)
			// 我们对应字节相加就可以了，所以我们可以通过位移的方法，让每个字节按照 uint8 的尺寸来解释，然后加上网段中对应字节的原始值
			for i := 0; i < 4; i++ {
				shiftBit := (3 - i) * 8 // 24 16 8
				ip[i] = uint8(ip[i]) + uint8(pos>>shiftBit)
			}

			// 将字符串中该位置标识为已分配；得转成字节切片操作，因为字符串是字面量，不允许修改
			ipAllocByte := []byte(handler.Subnets[subnetStr])
			ipAllocByte[pos] = '1'
			handler.Subnets[subnetStr] = string(ipAllocByte)
			// 落盘到文件中
			subnetsByte, _ := json.Marshal(handler.Subnets)
			err := dump(subnetsByte)
			if err != nil {
				return nil, err
			}
			break
		}
	}

	return
}

func (handler *SimpleIPHandler) Release(subnet *net.IPNet, ipaddr net.IP) error {
	// 直接把当前handler上的数据修改，然后落盘
	subnetMap := handler.Subnets
	subnetItem, exist := subnetMap[subnet.String()]
	if !exist {
		log.Printf("目标IP:%s不存在\n", ipaddr.String())
		return nil
	}

	ipAlloc := []byte(subnetItem) // 要修改的目标串

	// 确定目标IP所处的索引
	log.Printf("Release IP:%s\n", ipaddr.String())
	// 核心是针对每一个byte都减去被掩码掩盖的段地址
	pos := 0
	for i := 0; i < 4; i++ {
		diff := uint8(ipaddr[i]) - uint8(subnet.IP[i])
		shiftLeftBit := (3 - i) * 8
		pos += int(diff << shiftLeftBit)
	}
	ipAlloc[pos] = '0'
	subnetMap[subnet.String()] = string(ipAlloc)
	// 落盘
	dumpSubnetListByte, err := json.Marshal(subnetMap)
	if err != nil {
		return err
	}
	err = dump(dumpSubnetListByte)
	if err != nil {
		return err
	}

	return nil
}

// 子网段是否已经被占用
// IsSubnetInUse 检查给定的子网段是否已经在当前机器上占用
func IsSubnetInUse(subnet *net.IPNet) error {
	interfaces, err := net.Interfaces()
	if err != nil {
		return fmt.Errorf("error fetching network interfaces: %v", err)
	}

	for _, iface := range interfaces {
		addrs, err := iface.Addrs()
		if err != nil {
			log.Printf("Error fetching addresses for interface %s: %v\n", iface.Name, err)
			continue
		}

		for _, addr := range addrs {
			if ipnet, ok := addr.(*net.IPNet); ok {
				// 检查是否为环回地址或无实际意义的地址
				if ipnet.IP.IsLoopback() || ipnet.IP.IsLinkLocalUnicast() || ipnet.IP.IsLinkLocalMulticast() {
					continue
				}

				// 检查子网是否匹配
				if subnet.Contains(ipnet.IP) && sameMask(ipnet.Mask, subnet.Mask) {
					return fmt.Errorf("subnet %v overlaps with address on interface %s: %v", subnet, iface.Name, ipnet)
				}
			}
		}
	}

	return nil
}

// sameMask 检查两个子网掩码是否相同
func sameMask(mask1, mask2 []byte) bool {
	return string(mask1) == string(mask2)
}
